package edu.cmu.cs211.chess.unittested;

import edu.cmu.cs211.chess.board.ArrayBoard;
import edu.cmu.cs211.chess.board.ArrayPiece;
import edu.cmu.cs211.chess.evaluation.Evaluator;
import edu.cmu.cs211.chess.util.Iteratorable;

public class TestedEvaluator implements Evaluator<ArrayBoard>
{

	private static final int INFINITY  = 1000000;
	private static final int MATE      = 300000;
	private static final int STALEMATE = 0;

	public int infty()
	{
		return INFINITY;
	}

	public int mate()
	{
		return MATE;
	}

	public int stalemate()
	{
		return STALEMATE;
	}

	/*
	 * This is the evaluator. It simply returns a score for the board position
	 * with respect to the player to move. It must function precisely as
	 * described here in order to pass the unit tests. [If you want to use a
	 * different evaluation function for a more advanced version of your
	 * program, you can do that, but the eval() method here must function as
	 * described.]
	 * 
	 * The evaluation function gives a score for each piece according to the
	 * pieceValue array below, and an additional amount for each piece depending
	 * on where it is (see comment below). A bonus of 10 points should be given
	 * if the current player has castled (and -10 for the opponent castling)
	 * 
	 * The eval of a position is the value of the pieces of the player whose
	 * turn it is, minus the value of the pieces of the other player (plus the
	 * castling points thrown in).
	 * 
	 * If it's WHITE's turn, and white is up a queen, then the value will be
	 * roughly 900. If it's BLACK's turn and white is up a queen, then the value
	 * returned should be about -900.
	 */

	public int eval(ArrayBoard board)
	{
		int turn = board.toPlay();
		// evaluate the board for both players
		return getValue(turn,board) - getValue(1-turn, board);
	}

	private int getValue(int turn, ArrayBoard board)
	{
		int res = 0;
		Iteratorable<ArrayPiece>  pieces = board.allPiecesOfColor(turn);
		
		// loop thru all pieces of the current player
		// add corresponding values to the result
		while(pieces.hasNext()){
			ArrayPiece temp = pieces.next();
			int type = ArrayPiece.typeOfPiece(temp.piece);
			switch(type){
			case ArrayPiece.BISHOP:
				if(turn == ArrayBoard.WHITE)
					res += TestedEvaluator.bishoppos[temp.row()][temp.col()];
				else	// flip the board if player's black
					res += TestedEvaluator.bishoppos[7-temp.row()][temp.col()];
				res += TestedEvaluator.bishopval;
				break;
			case ArrayPiece.KNIGHT:
				if(turn == ArrayBoard.WHITE)
					res += TestedEvaluator.knightpos[temp.row()][temp.col()];
				else	// flip the board if player's black
					res += TestedEvaluator.knightpos[7-temp.row()][temp.col()];
				res += TestedEvaluator.knightval;
				break;
			case ArrayPiece.PAWN:
				if(turn == ArrayBoard.WHITE)
					res += TestedEvaluator.pawnpos[temp.row()][temp.col()];
				else	// flip the board if player's black
					res += TestedEvaluator.pawnpos[7-temp.row()][temp.col()];
				res += TestedEvaluator.pawnval;
				break;
			case ArrayPiece.KING:
				res += TestedEvaluator.kingval;
				break;
			case ArrayPiece.QUEEN:
				res += TestedEvaluator.queenval;
				break;
			case ArrayPiece.ROOK:
				res += TestedEvaluator.rookval;
				break;
			default:
				throw new IllegalArgumentException();
			}
		}
		
		if(board.hasCastled[turn])
			res += TestedEvaluator.CASTLE_BONUS;
		return res;
		
	}
	/*
	 * Piece value tables modify the value of each piece according to where it
	 * is on the board.
	 * 
	 * To orient these tables, each row of 8 represents one row (rank) of the
	 * chessboard.
	 * 
	 * !!! The first row is where white's pieces start !!!
	 * 
	 * So, for example
	 * having a pawn at d2 is worth -5 for white. Having it at d7 is worth
	 * 20. Note that these have to be flipped over to evaluate black's pawns
	 * since pawn values are not symmetric.
	 */
	private static int bishoppos[][] =
	{
		{-5, -5, -5, -5, -5, -5, -5, -5},
		{-5, 10,  5,  8,  8,  5, 10, -5},
		{-5,  5,  3,  8,  8,  3,  5, -5},
		{-5,  3, 10,  3,  3, 10,  3, -5},
		{-5,  3, 10,  3,  3, 10,  3, -5},
		{-5,  5,  3,  8,  8,  3,  5, -5},
		{-5, 10,  5,  8,  8,  5, 10, -5},
		{-5, -5, -5, -5, -5, -5, -5, -5}
	};
	private static int knightpos[][] =
	{
		{-10, -5, -5, -5, -5, -5, -5,-10},
		{ -8,  0,  0,  3,  3,  0,  0, -8},
		{ -8,  0, 10,  8,  8, 10,  0, -8},
		{ -8,  0,  8, 10, 10,  8,  0, -8},
		{ -8,  0,  8, 10, 10,  8,  0, -8},
		{ -8,  0, 10,  8,  8, 10,  0, -8},
		{ -8,  0,  0,  3,  3,  0,  0, -8},
		{-10, -5, -5, -5, -5, -5, -5,-10}
	};
	private static int pawnpos[][] =
	{
		{0,  0,  0,  0,  0,  0,  0,  0},
		{0,  0,  0, -5, -5,  0,  0,  0},
		{0,  2,  3,  4,  4,  3,  2,  0},
		{0,  4,  6, 10, 10,  6,  4,  0},
		{0,  6,  9, 10, 10,  9,  6,  0},
		{4,  8, 12, 16, 16, 12,  8,  4},
		{5, 10, 15, 20, 20, 15, 10,  5},
		{0,  0,  0,  0,  0,  0,  0,  0}
	};

	/* Material value of a piece */
	private static final int kingval      = 350;
	private static final int queenval     = 900;
	private static final int rookval      = 500;
	private static final int bishopval    = 300;
	private static final int knightval    = 300;
	private static final int pawnval      = 100;
	//private static final int emptyval     = 0;

	/* The bonus for castling */
	private static final int CASTLE_BONUS = 10;
}
